﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
using Seagull.Alerts;
using Seagull.Configuration;
using Seagull.DAL;
using Seagull.DAL.DriveManagers;
using Seagull.Events;
using Seagull.Generic;
using Seagull.Jobs;
using Seagull.Model.Alerts;
using Seagull.Shell.Properties;
using Seagull.Shell.Utils;
using Seagull.ZHD1X;
using ThreadingTimer = System.Threading.Timer;

namespace Seagull.Shell
{
    static class Program
    {
        private static ThreadingTimer _minuteTimer;
        private static DateTime _lastTick = DateTime.Today.AddDays(-2);

        public static List<IModule> CriticalModules { get; private set; }

            /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        internal static void Main()
        {
            //            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
            RegisterExceptionEvent();
            LoadConfig();
            Start();
        }

        internal static void Start()
        {
            CriticalModules = new List<IModule>();

            SmsSendManager.Instance.Start();
            CriticalModules.Add(SmsSendManager.Instance);

            AlertManager.Instance.Start();
            CriticalModules.Add(AlertManager.Instance);

            JobManager.Instance.Start();
            LocalDriveManager.Instance.Start();
            RemovableDriveManager.Instance.Start();

            try
            {
                RealtimeFrameSetSource.Instance.StartReceivingData();
            }
            catch(SocketException exception)
            {
                AlertEvent.Instance.Publish(new ApplicationAlert(exception, "打开UDP实时数据端口"));
                MessageBox.Show(Resources.ChartView_ChartView_OpenUdpPortFailed, Resources.Error,
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            RealtimeDataMonitor.Instance.Start();
            NewDayEvent.Instance.Register(OnNewDay);
            Application.ApplicationExit += OnApplicationExit;
            StartTickTimer();
            Application.AddMessageFilter(new IdleTimerWatcher());
            Application.Run(new Main());
            StopTickTimer();
            RealtimeDataMonitor.Instance.Stop();
            RealtimeFrameSetSource.Instance.StopReceivingData();
            RealtimeFrameSetSource.Instance.Dispose();
            AlertManager.Instance.Stop();
            JobManager.Instance.Stop();
            RemovableDriveManager.Instance.Stop();
            LocalDriveManager.Instance.Stop();
            SmsSendManager.Instance.Stop();
        }

        private static void OnApplicationExit(object sender, EventArgs e)
        {
            ShutdownCriticalModules();
        }

        private static void StopTickTimer()
        {
            _minuteTimer.Change(-1, -1);
        }

        private static void StartTickTimer()
        {
            _minuteTimer = new ThreadingTimer(OnMinuteTimerTick, null, 6000, 60 * 1000);
        }

        private static void OnMinuteTimerTick(object state)
        {
            var now = DateTime.Now;
            var deltaNow = now - DateTime.MinValue;
            var deltaLast = _lastTick - DateTime.MinValue;

            var newMinute = (int)deltaNow.TotalMinutes != (int)deltaLast.TotalMinutes;
            var newHour = (int)deltaNow.TotalHours != (int)deltaLast.TotalHours;
            var newDayAlertTime = Config.Instance.TaskConfig.DailyTimeToSendNewDayAlert;
            var newDay = (now - newDayAlertTime).Date != (_lastTick - newDayAlertTime).Date;
            _lastTick = now;

            if(newDay)
            {
                NewDayEvent.Instance.Publish(now);
            }
            if(newMinute)
            {
                NewMinuteEvent.Instance.Publish(now);
            }
            if(newHour)
            {
                NewHourEvent.Instance.Publish(now);
            }
        }

        private static bool _firstNewDayAlert = true;

        private static void OnNewDay(DateTime time)
        {
            // 定时重启
            if(ShouldRestart)
            {
                Settings.Default.Restarting = true;
                Settings.Default.Save();
                Application.Restart();
            }
            else if(_firstNewDayAlert)
            {
                // 系统刚启动
                if(Settings.Default.Restarting)
                {
                    // 这是一次自动重启
                    AlertEvent.Instance.Publish(new RestartedAlert());

                    Settings.Default.Restarting = false;
                    Settings.Default.Save();
                }
                else
                {
                    // 这是手动启动
                    AlertEvent.Instance.Publish(new StartupAlert());
                }
                _firstNewDayAlert = false;
            }
            else
            {
                // 例行报警
                AlertEvent.Instance.Publish(new DailyAlert());
            }
        }

        private static bool ShouldRestart
        {
            get
            {
                return (DateTime.Now - Process.GetCurrentProcess().StartTime).TotalHours >
                    Config.Instance.TaskConfig.AutoRestartIntervalDays * 24 - 20;
            }
        }

        private static void LoadConfig()
        {
            try
            {
                Config.Load(ConfigurationManager.AppSettings["configFile"]);
            }
            catch(Exception exception)
            {
                MessageBox.Show(
                    string.Format("加载配置文件失败！请检查配置文件。详细信息：{0}", exception.ToString()), 
                    "配置文件错误", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                Environment.Exit(-1);
            }
        }

        #region Unhandled Exception Process

        private static void RegisterExceptionEvent()
        {
            Application.ThreadException += OnThreadException;
            AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
        }

        private static void OnThreadException(object sender, ThreadExceptionEventArgs e)
        {
            AlertEvent.Instance.Publish(new ApplicationAlert(e.Exception, "界面操作"));
        }

        private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            OnException((Exception)e.ExceptionObject);
        }

        private static void OnException(Exception exception)
        {
            var message = string.Format("GMS错误，即将自动退出并尝试重启: \n{0}", exception);
            ThreadPool.QueueUserWorkItem(t =>
            {
                Thread.Sleep(5000);
                ShutdownCriticalModules();
                Process.Start(Application.ExecutablePath);
                Environment.FailFast(message, exception);
            });
            AlertEvent.Instance.Publish(new ApplicationAlert(exception));
            MessageBox.Show(message);
        }

        #endregion

        private static void ShutdownCriticalModules()
        {
            foreach(var module in CriticalModules)
            {
                try
                {
                    module.Stop();
                }
                catch(Exception)
                {
                    // Ignore
                }
            }
        }

    }
}
